package exchange

import (
	"strings"
	"sync/atomic"
	"time"

	"gitee.com/lyuanbo/stock-plus/pkg/logger"
	"gitee.com/lyuanbo/stock-plus/pkg/stock/types"
	"github.com/jinzhu/copier"
)

// 交易所代码常量
const (
	SH     = "SH"     // 上海证券交易所
	SZ     = "SZ"     // 深圳证券交易所
	HK     = "HK"     // 香港交易所
	NASDAQ = "NASDAQ" // 纳斯达克交易所
)

type DynamicExchange struct {
	val atomic.Value

	logger *logger.Logger

	api types.Stocker
}

func New(logger *logger.Logger, api types.Stocker) *DynamicExchange {
	d := &DynamicExchange{
		logger: logger,
		api:    api,
	}

	var m map[string]*types.Exchange
	_ = copier.CopyWithOption(&m, &defaultExchanges, copier.Option{IgnoreEmpty: true, DeepCopy: true})

	d.val.Store(m)

	d.MonitorTradingStatus()

	return d
}

var defaultExchanges = map[string]*types.Exchange{
	SH:     {Code: "SH", Name: "上海证券交易所", TimeZone: "Asia/Shanghai", Currency: "人民币", Symbol: "SH000001"},
	SZ:     {Code: "SZ", Name: "深圳证券交易所", TimeZone: "Asia/Shanghai", Currency: "人民币", Symbol: "SZ399001"},
	HK:     {Code: "HK", Name: "香港交易所", TimeZone: "Asia/Hong_Kong", Currency: "港币", Symbol: "HKHSI"},
	NASDAQ: {Code: "NASDAQ", Name: "纳斯达克交易所", TimeZone: "America/New_York", Currency: "美元", Symbol: ".IXIC"},
}

func (d *DynamicExchange) Get(ex string) *types.Exchange {
	v, ok := d.val.Load().(map[string]*types.Exchange)[strings.ToUpper(ex)]
	if !ok {
		return &types.Exchange{Code: "Un", Name: "未知交易所", Status: "未开盘", TimeZone: "Asia/Shanghai", Currency: "人民币", Symbol: ""}
	}
	return v
}

func (d *DynamicExchange) IsMarketOpen(ex string) bool {
	return d.Get(strings.ToUpper(ex)).Status == "交易中"
}

func (d *DynamicExchange) MonitorTradingStatus() {
	go func() {
		for {
			t1 := d.val.Load().(map[string]*types.Exchange)
			// 拷贝一份，防止并发修改导致数据不一致
			var m = make(map[string]*types.Exchange)
			_ = copier.CopyWithOption(&m, &t1, copier.Option{IgnoreEmpty: true, DeepCopy: true})

			var symbols []string
			for _, v := range m {
				symbols = append(symbols, v.Symbol)
			}

			var isChange bool
			data := d.api.GetExchange(symbols...)

			for _, item := range data {
				if !isChange && m[item.Code].Status != item.Status {
					isChange = true
				}
				m[item.Code].Status = item.Status
			}

			if isChange {
				d.val.Store(m)
			}

			time.Sleep(5 * time.Second)
		}
	}()
}
